
local itercache = require 'dclm.cache.iterator'
local dclm = require 'dclm'
local data = require 'dclm.text.unicodedata.unicodedata_all'
local compose = require 'dclm.text.unicodedata.compose_table'
local print
    = print
local yield
    = coroutine.yield
local floor
    = math.floor


module (...)


function name(charcode)
  local entry = data[charcode]
  return entry and entry.name
end

function decimal(charcode)
  local entry = data[charcode]
  return entry and entry.decdigit
end

function digit(charcode)
  local entry = data[charcode]
  return entry and entry.digit
end

function numeric(charcode)
  local entry = data[charcode]
  return entry and entry.numeric
end

function category(charcode)
  local entry = data[charcode]
  return entry and entry.cat
end

function mirrored(charcode)
  local entry = data[charcode]
  return entry and entry.mirror or false
end

local function eachtoupper_aux(eachchar, ...)
  for c in eachchar, ... do
    local entry = data[c]
    local upper = entry and entry.upper
    if upper then
      yield(upper)
    else
      yield(c)
    end
  end
end

function eachtoupper(eachchar, ...)
  return itercache.get(eachtoupper_aux, eachchar, ...)
end

local function eachtolower_aux(eachchar, ...)
  for c in eachchar, ... do
    local entry = data[c]
    local lower = entry and entry.lower
    if lower then
      yield(lower)
    else
      yield(c)
    end
  end
end

function eachtolower(eachchar, ...)
  return itercache.get(eachtolower_aux, eachchar, ...)
end

local function decomposecanon(c)
  local entry = data[c]
  if not decomp.compat then
  end
end

local function yield_decompose(compat, c)
  -- Begin: Special Decomposition for Hangul
  -- http://unicode.org/reports/tr15/#Hangul
  if (c >= 0xAC00) and (c < 0xD7A4) then
    local SIndex = c - 0xAC00
    yield(0x1100 + floor(SIndex / 588))
    yield(0x1161 + floor((SIndex % 588) / 28))
    local t = SIndex % 28
    if (t ~= 0) then
      yield(0x11A7 + t)
    end
    return
  end
  -- End: Special Decomposition for Hangul
  
  local entry = data[c]
  local decomp = entry and entry.decomp
  if decomp and (compat or not decomp.compat) then
    for i = 1, #decomp do
      yield_decompose(compat, decomp[i])
    end
  else
    yield(c)
  end
end

local function eachdecompose_aux(compat, eachchar, ...)
  for c in eachchar, ... do
    yield_decompose(compat, c)
  end
end

local function canon_gt(a, b)
  return data[a].combo > data[b].combo
end

local function reorder(compat, eachchar, ...)
  local iter = itercache.get(eachdecompose_aux, compat, eachchar, ...)
  while true do
    local c = iter()
    if (c == nil) then
      return
    end
    local info = data[c]
    local combo = info and info.combo or 0
    if (combo == 0) then
      yield(c)
    else
      local chars = {c}
      local c2
      while true do
        c2 = iter()
        if (c2 == nil) then
          break
        end
        local c2_info = data[c2]
        if (c2_info.combo or 0) == 0 then
          break
        else
          chars[#chars+1] = c2
        end
      end
      chars = dclm.mergesort(chars, canon_gt)
      for i = 1, #chars do
        yield(chars[i])
      end
      if c2 then
        yield(c2)
      else
        return
      end
    end
  end
end

function eachdecompose(compat, eachchar, ...)
  return itercache.get(reorder, compat, eachchar, ...)
end


local function compose_aux(eachchar, ...)
  local buf = {}
  local buf_sz = 0
  local bcompose
  for c in eachchar, ... do
    local info = data[c]
    local combo = info and info.combo or 0
    -- Begin: Special Composition for Hangul
    -- http://unicode.org/reports/tr15/#Hangul
    if (c >= 0x1161) and (c <= 0x1175) and (buf_sz == 1) and (buf[1] >= 0x1100) and (buf[1] <= 0x1113) then
      local combined = 0xAC00 + ((buf[1] - 0x1100)*21 + (c - 0x1161)) * 28
      buf[1] = combined
      bcompose = compose[combined]
    elseif (c >= 0x11A7) and (c <= 0x11C2) and (buf_sz == 1) and (buf[1] >= 0xAC00) and (buf[1] <= 0xD7A3) and (((buf[1] - 0xAC00) % 28)==0) then
      local combined = buf[1] + (c - 0x11A7)
      buf[1] = combined
      bcompose = compose[combined]
    -- End: Special Composition for Hangul
    elseif (combo == 0) then
      local combined = (buf_sz == 1) and bcompose and bcompose[c]
      if combined then
        buf[1] = combined
        bcompose = compose[combined]
      else
        for i = 1, buf_sz do
          yield(buf[i])
        end
        buf_sz = 1
        buf[1] = c
        bcompose = compose[c]
      end
    elseif buf_sz == 0 then
      yield(c)
    else
      for i = buf_sz, 1, -1 do
        if (i == 1) then
          local combined = bcompose and bcompose[c]
          if combined then
            buf[1] = combined
            bcompose = compose[combined]
          else
            buf_sz = buf_sz + 1
            buf[buf_sz] = c
          end
        else
          local p_info = data[buf[i]]
          if p_info.combo == combo then
            buf_sz = buf_sz + 1
            buf[buf_sz] = c
            break
          end
        end
      end
    end
  end
  for i = 1, buf_sz do
    yield(buf[i])
  end
end

function eachcompose(eachchar, ...)
  return itercache.get(compose_aux, eachchar, ...)
end
